package app

import (
	"strconv"
	"sync"

	"gitee.com/leicc/go-imsrv-app/app/logic"
	"gitee.com/leicc/go-orm/log"
)

type Agent struct {
	Clients sync.Map //map[*Client]int64
	Users sync.Map   //map[int64][]*Client
	NSize int		 //统计总的连接数量
	Appid int64      //应用配置ID，服务与哪个应用业务
}

var GState *Agent = nil
func NewAgent(appid int64) *Agent {
	GState = &Agent{
		Clients : sync.Map{},
		Users: sync.Map{},
		NSize: 0,
		Appid: appid,
	}
	return GState
}

//将新的请求连接注册到agent状态管理当中
func (a *Agent) Register(userid int64, client *Client) {
	a.Clients.Store(client, userid)
	if userid > 0 {//注册userid到句柄的映射
		var uClients []*Client = nil
		if sClient, ok := a.Users.Load(userid); !ok {
			uClients = make([]*Client, 0)
		} else {
			uClients = sClient.([]*Client)
		}
		uClients = append(uClients, client)
		a.Users.Store(userid, uClients)
		log.Write(log.DEBUG, userid, "用户上线了呀~")
	}
}

//断开连接的时候将句柄移除状态管理
func (a *Agent) UnRegister(client *Client) {
	userid := int64(0)
	value, ok := a.Clients.LoadAndDelete(client)
	if ok {//说明存储的状态存在的情况
		userid = value.(int64)
	}
	if userid > 0 { //说明用户已经登录
		if sClient, ok := a.Users.Load(userid); ok {
			uClients := sClient.([]*Client)
			for idx, tmpClient := range uClients {
				if tmpClient == client {
					uClients = append(uClients[:idx], uClients[idx+1:]...)
					break
				}
			}
			if len(uClients) < 1 { //标记redis离线
				logic.RState.OffLine(a.Appid, userid)
				a.Users.Delete(userid)
			} else {
				a.Users.Store(userid, uClients)
			}
		}
		log.Write(log.DEBUG, userid, "用户离线了呀~")
	}
}

//判定用户是否已经连接IM
func (a *Agent) GetUserId(client *Client) int64 {
	value, ok := a.Clients.Load(client)
	if ok {
		userid := value.(int64)
		if userid > 0 {
			return userid
		}
	}
	return -1
}

//给指定的用户推送消息接口
func (a *Agent) PushMessageToUser(userid int64, message []byte) int {
	nsize := 0
	if sClient, ok := a.Users.Load(userid); ok {
		uClients := sClient.([]*Client)
		for _, client := range uClients {
			client.Send(message)
			nsize++
		}
	}
	return nsize
}

//推送频道数据信息资料
func (a *Agent) PushMessageToChannel(userid, chnid int64, message []byte) int {
	nsize := 0
	chanMembers := logic.RState.ChanMembers(a.Appid, chnid)
	if chanMembers != nil && len(chanMembers) > 0 {//成员数量大于1的情况
		for _, uidStr := range chanMembers {
			toUserId, err := strconv.ParseInt(uidStr, 10, 64)
			if err == nil && toUserId != userid {//排除发送者推送消息
				nsize += a.PushMessageToUser(toUserId, message)
			}
		}
	}
	return nsize
}

//发送广播消息给所有的用户
func (a *Agent) PushMessageToAll(c *Client, message []byte) int {
	nsize := 0
	a.Clients.Range(func(cliInterface, _ interface{}) bool {
		client := cliInterface.(*Client)
		if c == nil || c != client {//排除发送者自己
			client.Send(message)
			nsize++
		}
		return true
	})
	return nsize
}

//消费队列当中的消息回调处理逻辑
func CallbackNotify(appid int64, args *logic.QueueMessageSt)  {
	//状态是在线的直接推送消息 私聊的处理逻辑
	if (args.Type == 0 || args.Type == 3) && logic.RState.IsOnline(appid, args.ToId) {
		nsize := GState.PushMessageToUser(args.ToId, args.Message)
		log.Write(log.INFO, "MQ C2C给用户{",args.ToId,"}推送{",nsize,"}条消息")
	} else if args.Type == 1 && args.ToId > 0 {//频道聊天的处理逻辑
		nsize := GState.PushMessageToChannel(args.UserId, args.ToId, args.Message)
		log.Write(log.INFO, "MQ频道{",args.ToId,"}推送{",nsize,"}条消息")
	} else if args.Type == 2 {//给系统全局的用户发送消息推送
		nsize := GState.PushMessageToAll(nil, args.Message)
		log.Write(log.INFO, "MQ给世界频道推送{",nsize,"}条消息")
	} else {
		log.Write(log.INFO, "MQ处理用户离线,未发送任何消息.")
	}
}